﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Timers;
using GeneralDFS.BusinessLogical.Nodes;
using GeneralDFS.BusinessLogical.FileSystemLogical;
using GeneralDFS.DataEntity.Enum;
using GeneralDFS.DataEntity.IO;
using GeneralDFS.DataEntity.Nodes;
using GeneralDFS.DataEntity.Config;

namespace GeneralDFS.BusinessLogical.IO
{
     [Serializable]
    public class BlockManager
    {
        private readonly NameNode _nameNode;
        private readonly DataNodeCenter _dataNodeCenter;
        private readonly FileSystem _fileSystem;
        private readonly Timer _timerManagerBlock;
        private readonly DFSConfig _config;
        private readonly BlockMap _blockMap;

        public BlockManager(NameNode nameNode, DataNodeCenter dataNodeCenter)
        {
            _nameNode = nameNode;
            _fileSystem = nameNode.FileSystem;
            _dataNodeCenter = dataNodeCenter;
            _timerManagerBlock=new Timer(10*30);
            _config = nameNode.Config;
            _blockMap=new BlockMap();
        }

        public BlockMap GetBlockMap()
        {
            return _blockMap;
        }

        public void Start()
        {
            _timerManagerBlock.Elapsed += _timerManagerBlock_Elapsed;
            _timerManagerBlock.Start();
        }
        
        /// <summary>
        /// 定时检查Block的拷贝数量，不符合要求则发送复制请求
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _timerManagerBlock_Elapsed(object sender, ElapsedEventArgs e)
        {
            if(_nameNode.LocalNode.GetNodeState()<=NameNodeState.SafeMode)
                return;
            var dataNodeList = _dataNodeCenter.GetDataNodeInfoList();
            foreach (var dataNodeInfoWithBlock in dataNodeList)
            {
                var blockIterator = new BlockIterator(dataNodeInfoWithBlock.GetFileBlockList(), dataNodeInfoWithBlock);
                foreach (FileBlockInfo block in blockIterator)
                {
                    if (dataNodeInfoWithBlock.State == DataNodeState.Offline ||
                        dataNodeInfoWithBlock.State == DataNodeState.UNormal)
                    {
                        block.RemoveNode(dataNodeInfoWithBlock);
                    }
                    var hasCopy = block.NumNodes();
                    if (block.HasCopyed || hasCopy >= _config.DetailConfig.CopyTimes || hasCopy >= dataNodeList.Count)
                        continue;
                    block.HasCopyed = true;
                    _dataNodeCenter.CopyToDataNode(block, _config.DetailConfig.CopyTimes - hasCopy);
                }
            }
        }

        public void Stop()
        {
            _timerManagerBlock.Stop();
            _blockMap.Close();
        }

        /// <summary>
        /// 处理Block列表
        /// </summary>
        /// <param name="dataNodeID"></param>
        /// <param name="fileBlockMetaDatas"></param>
        public void ProcessBlockReport(DataNodeID dataNodeID, List<FileBlockMetaData> fileBlockMetaDatas)
        {
            _fileSystem.WriteLock();
            var now = DateTime.Now;
            var dataNode = _dataNodeCenter.GetDataNodeByID(dataNodeID);
            if (dataNode != null&&_nameNode.LocalNode.GetNodeState()==NameNodeState.SafeMode)
            {
                if (dataNode.NumCount() == 0)
                {
                    foreach (var fileBlockMetaData in fileBlockMetaDatas)
                    {
                        var fileBlockInfo = _blockMap.GetStoredBlock(fileBlockMetaData);
                        if(fileBlockInfo==null) continue; //该文件块不属于任何节点

                        AddStoredBlock(fileBlockInfo,dataNode);
                    }
                }
            }
            var end = DateTime.Now;
            _fileSystem.WriteUnlock();
            Debug.Assert(dataNode != null);
            LogUtil.LogInfo("ProcessBlockReport ServerName:{0} numOfBlock:{1} time:{2}",dataNodeID.Name,dataNode.NumCount(),end.Subtract(now).TotalSeconds);
        }

        public void AddStoredBlock(FileBlockInfo fileBlockInfo, DataNodeInfoWithBlocks dataNodeInfo)
        {
            _fileSystem.WriteLock();
            dataNodeInfo.AddBlock(fileBlockInfo);
            _fileSystem.WriteUnlock();
        }
    }
}
